#include <gl\glew.h>
#include <gl\freeglut.h>
#include "..\VectorMath\VectorMath.h"

#include <iostream> //adding this to be able to output debug information to the console

#pragma comment (lib, "glew32.lib")
#pragma comment (lib, "freeglut.lib")


//glob_al variables (each glob_al variable has the "glob_" prefix to optimize for IntelliSense - just write glob_ and pres Alt + Left Arrow and you'll see all glob_al variables)
bool glob_KeyStates[256]; //we have 256 character codes which we receive as information for the keyboard

int glob_WindowPositionX = 50,
	glob_WindowPositionY = 50,
	glob_WindowSizeX = 800,
	glob_WindowSizeY = 800;

double glob_AngleOfView_degs = 60,
	glob_NearPlaneDist = 1,
	glob_FarPlaneDist = 1000,
	glob_WorldUpVectorX = 0,
	glob_WorldUpVectorY = 1,
	glob_WorldUpVectorZ = 0;


//camera variables
double glob_CamPosX = 30,
	glob_CamPosY = 20,
	glob_CamPosZ = 0,
	glob_CamLookX = 0,
	glob_CamLookY = 0,
	glob_CamLookZ = 0;

Vector3 glob_CamPos,
	glob_CamLook;
//----------------

//sphere class
class Sphere
{
	Vector3 center;
	double radius;
	double colorR, colorG, colorB;
	bool isColliding;
public:
	Sphere (Vector3 center = Vector3(), double radius = 0, double colorR = 0, double colorG = 0, double colorB = 0)
	{
		this->center = center;
		this->radius = radius;
		this->colorR = colorR;
		this->colorG = colorG;
		this->colorB = colorB;
	}

	Vector3 GetCenter()
	{
		return this->center;
	}

	double GetRadius()
	{
		return this->radius;
	}

	void DetectCollision (Sphere other)
	{
		Vector3 centerToCenterVector = this->center - other.center;
		double sqDistance = centerToCenterVector.GetSqLength();

		double distance = centerToCenterVector.GetLength();

		double sqRadiusSum =  (this->radius + other.radius) * (this->radius + other.radius);

		/*
		if(sqRadiusSum > sqDistance)
		{
			this->isColliding = true;
		}
		else
		{
			this->isColliding = false;
		}*/
		
		if(distance < this->radius + other.radius)
		{
			this->isColliding = true;
		}
		else
		{
			this->isColliding = false;
		}
	}

	void Render()
	{
		glPushMatrix();
		glTranslatef(this->center.GetX(), this->center.GetY(), this->center.GetZ());
		if(!this->isColliding)
		{
			glColor3f (this->colorR, this->colorG, this->colorB);
		}
		else
		{
			glColor3f (1.0, 0.0, 0.0);
		}
		glutSolidSphere(this->radius, 100, 100);
		glPopMatrix();
	}

	void MoveX(double units)
	{
		this->center.SetX (this->center.GetX() + units);
	}

	void MoveZ(double units)
	{
		this->center.SetZ (this->center.GetZ() + units);
	}
};

Sphere glob_sphereStatic(Vector3(), 10, 1, 1, 1),
	glob_sphereMoving(Vector3(18, 0, 0), 3, 1, 1, 1);

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void ReshapeWindow(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();

	double aspectRatio = ((float)width) / ((float)height);

	gluPerspective(glob_AngleOfView_degs, aspectRatio, glob_NearPlaneDist, glob_FarPlaneDist);
	glMatrixMode (GL_MODELVIEW);
}

void HandleKeyDown (unsigned char key, int cursorX, int cursorY)
{

	glob_KeyStates[key] = true;
}

void HandleKeyUp (unsigned char key, int cursorX, int cursorY)
{
	glob_KeyStates[key] = false;
}

void HandleKeyboard()
{
	if(glob_KeyStates['s'] || glob_KeyStates['S'])
	{
		glob_sphereMoving.MoveX(0.05);
	}
	if(glob_KeyStates['w'] || glob_KeyStates['W'])
	{
		glob_sphereMoving.MoveX(-0.05);
	}

	if(glob_KeyStates['a'] || glob_KeyStates['A'])
	{
		glob_sphereMoving.MoveZ(0.05);
	}
	if(glob_KeyStates['d'] || glob_KeyStates['D'])
	{
		glob_sphereMoving.MoveZ(-0.05);
	}
}

void ApplyCamera(double camPosX, double camPosY, double camPosZ, double camLookX, double camLookY, double camLookZ)
{
	gluLookAt(camPosX, camPosY, camPosZ, camLookX, camLookY, camLookZ, glob_WorldUpVectorX, glob_WorldUpVectorY, glob_WorldUpVectorZ);
}

void DrawObjects()
{
	glob_sphereMoving.Render();
	glob_sphereStatic.Render();
}

void Display()
{
	glClearColor(0.0, 0.0, 0.0, 1.0); //set to what color to clear the color buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear old information from the color and depth buffer
	glLoadIdentity(); //initialize the identity matrix (default the orientation)

	ApplyCamera(glob_CamPosX, glob_CamPosY, glob_CamPosZ, glob_CamLookX, glob_CamLookY, glob_CamLookZ);
	DrawObjects();

	glutSwapBuffers(); //send what we have drawn to the screen
}

void MainLoop()
{
	HandleKeyboard();
	Display();
	for(int i=0; i<=100000; i++)
	{
		glob_sphereMoving.DetectCollision(glob_sphereStatic);
	}
}

void InitializeWindow()
{
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); //we need double buffering, depth buffering and a RGBA color scheme
	glutInitWindowPosition(glob_WindowPositionX, glob_WindowPositionY);
	glutInitWindowSize(glob_WindowSizeX, glob_WindowSizeY);

	glutCreateWindow("Sphere-Sphere collision detection");
}

void InitializeGL()
{
	glEnable(GL_DEPTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
}

void RegisterFunctions()
{
	glutIdleFunc(MainLoop);
	glutDisplayFunc(Display);
	glutReshapeFunc(ReshapeWindow);
	glutKeyboardFunc(HandleKeyDown);
	glutKeyboardUpFunc(HandleKeyUp);
}

int main (int argc, char ** argv)
{
	glutInit(&argc, argv);
	InitializeWindow();
	InitializeGL();
	RegisterFunctions();

	glutMainLoop();
}